package com.yunc.base.netty;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunc.base.utils.R;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * netty长连接handler实现
 * 本地存储连接的 channel，如果要找到某一个连接，就使用 MQ 进行广播，所有客户端拿到消息后，在本地进行查找，如果有 channel，则进行通讯
 * rabbitmq中消费者声明一个系统产生的随机队列绑定到这个交换机上，然后往交换机发消息，只要绑定到这个交换机上都能收到消息
 *
 * @author taocongcong
 * @create 2019-03-12 14:47
 */
@Slf4j
public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    // private final static ISysUserService sysUserService = (ISysUserService) SpringContextUtils.getBean("sysUserService");

    /**
     * key用户id，一个用户在多个地方登陆
     */
    public static Map<String, ChannelGroup> userChannelMap = new HashMap<>();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        // 自定义协议
        JSONObject object = JSON.parseObject(msg.text());
        // 1=心跳监测
        String s = "";
        if (StringUtils.equals(object.getString("type"), "1")) {
            s = JSONObject.toJSONString(R.ok("ok"));
        }
        // 2=登录创建连接，如果已存在替换
        if (StringUtils.equals(object.getString("type"), "2")) {
            String userId = (String)object.get("data");
            if (StringUtils.isNotEmpty(userId)) {
                if (userChannelMap.containsKey(userId)) {
                    ChannelGroup channelGroup = userChannelMap.get(userId);
                    channelGroup.add(ctx.channel());
                    userChannelMap.put(userId, channelGroup);
                } else {
                    ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
                    channelGroup.add(ctx.channel());
                    userChannelMap.put(userId, channelGroup);
                }
            }
            s = JSONObject.toJSONString(R.ok("登录"));
        }
        if (StringUtils.equals(object.getString("type"), "3")) {
            String userId = (String)object.get("data");
            if (StringUtils.isNotEmpty(userId)) {
                if (userChannelMap.containsKey(userId)) {
                    ChannelGroup channelGroup = userChannelMap.get(userId);
                    channelGroup.remove(ctx.channel());
                }
            }
            s = JSONObject.toJSONString(R.ok("退出"));
        }
        if (StringUtils.equals(object.getString("type"), "3")) {
            s = JSONObject.toJSONString(R.ok("首页展示"));
        }
        // 读取收到的信息写回到客户端,ctx.channel是属于一个通道的
        ctx.channel().writeAndFlush(new TextWebSocketFrame(s));
    }

    /**
     * 连接建立时
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        System.out.println("handlerAddred " + ctx.channel().id().asLongText());
        // 获取连接的channel
        Channel incomming = ctx.channel();
        log.info("[SERVER]-"+incomming.remoteAddress()+"加入\n");
    }

    /**
     * 连接关闭时
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        System.out.println("handlerRemoved " + ctx.channel().id().asLongText());
        // 获取连接的channel
        Channel incomming = ctx.channel();
        log.info("[SERVER]-"+incomming.remoteAddress()+"离开\n");
        // 从服务端的channelGroup中移除当前离开的客户端
        // 关闭其他
        if (userChannelMap != null && userChannelMap.size() > 0) {
            for (Map.Entry<String, ChannelGroup> entry : userChannelMap.entrySet()) {
                ChannelGroup channelGroup = entry.getValue();
                if (channelGroup.contains(ctx.channel())) {
                    channelGroup.remove(ctx.channel());
                }
            }
        }
    }

    /**
     * 异常发生时
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("异常发生");
        ctx.close();
    }
}
